#include "../inc/mzrtos.h"
#include "../inc/mzhw.h"

static struct mz_list_node timer_list;

static void _mz_timer_init(mz_timer_t timer,
                           const char* name,
                           void (*entry)(void* arg),
                           void* arg,
                           mz_tick_t cycle,
                           mz_int32_t repeat){
    timer->entry = entry;
    timer->arg = arg;
    timer->cycle = cycle;
    timer->current_tick = 0;
    timer->repeat = repeat;

    /* Mount the timer object into the kernel object */
    mz_object_init(&timer->parent, name, MZ_OBJECT_TIMER);

    timer->parent.flag &= ~MZ_TIMER_ACTIVATED;

    mz_list_init(&timer->list);
}

void mz_timer_init(mz_timer_t timer,
                   const char* name,
                   void (*entry)(void* arg),
                   void* arg,
                   mz_tick_t cycle,
                   mz_int32_t repeat){
    mz_uint32_t level;
    level = mz_hw_interrupt_disable();

    _mz_timer_init(timer, name, entry, arg, cycle, repeat);

    mz_hw_interrupt_enable(level);
}

void mz_timer_start(mz_timer_t timer){
    mz_list_t l = &timer_list;
    mz_timer_t t = MZ_NULL;

    if(timer->repeat == 0)
        return;

    if(timer->cycle >= (MZ_TIMER_MAX_CYCLE / 2))
        return;

    mz_uint32_t level;
    level = mz_hw_interrupt_disable();

    timer->current_tick = mz_tick_get() + timer->cycle;

    //todo-jianglin: add 'skip list' function to promote speed
    for(; l->next != &timer_list; l = l->next){
        t = mz_list_entry(l->next,
                          struct mz_timer,
                          list);
        if((t->current_tick - timer->current_tick) < (MZ_TIMER_MAX_CYCLE / 2)){
            break;
        }
    }
    if(t){
        /*
         * If a timer with a longer timeout than the new timer is found,
         * then insert a new timer before this timer
         * */
        mz_list_insert_before(&t->list, &timer->list);
    }else{
        /* If not found, insert it at the end of the list */
        mz_list_insert_before(&timer_list, &timer->list);
    }
    /* Set to active state */
    timer->parent.flag |= MZ_TIMER_ACTIVATED;

    mz_hw_interrupt_enable(level);
}

void mz_timer_stop(mz_timer_t timer){
    mz_uint32_t level;
    level = mz_hw_interrupt_disable();

    mz_list_remove(&timer->list);
    /* Clear activity flag */
    timer->parent.flag &= ~MZ_TIMER_ACTIVATED;

    mz_hw_interrupt_enable(level);
}

void mz_timer_check(void){
    mz_list_t l = &timer_list;
    mz_timer_t t = MZ_NULL;

    for(; l->next != &timer_list; l = l->next){
        t = mz_list_entry(l->next,
                          struct mz_timer,
                          list);
        /*
         * Since the timer list is arranged in ascending order
         * the first timer does not time out
         * and the subsequent ones do not time out
         * */
        mz_tick_t system_tick = mz_tick_get();
        if(system_tick - t->current_tick <= (MZ_TIMER_MAX_CYCLE / 2)){
            t->entry(t->arg);
            if(t->repeat != MZ_TIMER_PERIODIC){
                t->repeat --;
            }
            if(t->repeat == 0){
                    t->current_tick = 0;
                    mz_list_remove(&t->list);
            }else{
                    t->current_tick = system_tick + t->cycle;
            }
            continue;
        }
        break;
    }
}

void mz_timer_system_init(void){
    mz_list_init(&timer_list);
}

#include <stdio.h>
void mz_timer_debug_sort(void){
    mz_list_t l = &timer_list;
    mz_timer_t t = MZ_NULL;

    for(; l->next != &timer_list; l = l->next){
        t = mz_list_entry(l->next,
                          struct mz_timer,
                          list);
        if(t != MZ_NULL){
            printf("%s:%ld\r\n", t->parent.name, t->current_tick);
        }
    }
}